
from TimeChessUtil import *


class Callable:
	"""
	Little wrapper to make 'static methods' possible.
	see: http://code.activestate.com/recipes/
		52304-static-methods-aka-class-methods-in-python/
	"""
	def __init__(self, anycallable):
		self.__call__ = anycallable

#############################################################
#						PIECE OBJECT						#
#############################################################
class Piece(object):
	def __init__(self, color, pos, game, pieceId=None, age=0):
		"""
		color should be either 'b' or 'w'
		pos is a Pos object
		"""
		self.color = color.lower()
		self.pos = pos
		self.age = age
		#self.char = "error: Piece of no type"
		#self.intkey = 0 #always overriden
		self.forwards = 1 if self.color == 'w' else -1


		# pieceId allows pieces to maintain identities between turns
		# They are NOT unique to Piece objects, but they are unique to pieces
		# in the game
		if pieceId is None:
			self.pieceId = "%s%s%s" % (self.color, self.char, self.pos.file)
		else:
			self.pieceId = pieceId

		# The `Game` object the piece is on
		self.game = game

		# Is this piece incapable continuing in time (i.e. has it jumped forwards)
		self.sterile = False

	def getMoves(self):
		"""Return a list of possible positions to move to"""
		#empty, implemented by subclass
		pass

	def setPos(self, newpos):
		self.pos = newpos

	def __str__(self):
		if self.color=='b': return self.char.lower()
#		else: return "\033[1m"+self.char.upper()+"\033[0;0m"
		else: return self.char.upper()
#		return "%c%c%c%c" % (self.color,self.char.upper(),self.pos.file,self.pos.rank)

	def humanString(self):
		if self.color=='b': col = "Black"
		else: col = "White"
		return "%s %s" % (col, self.name)

	def getMovesInDirections(self,directions):
		moves = []
		for dx, dy, dt in directions:
			nx = self.pos.x
			ny = self.pos.y
			nt = self.pos.t

			# for distances 1 to 7 inclusive
			for i in range(1,8):
				nx += dx
				ny += dy
				nt += dt

				npos = Pos(nx,ny,nt)
				if not npos.isOnBoard():
					break

				piece = self.game.getPieceAt(npos)
				if (piece is None) or (piece.color != self.color):
					moves.append(Move(self.pos,npos))
				else:
					break
		return moves

	def getIntKey(self):
		raise DeprecationWarning("The array no longer uses integers, "+
								"so this method is deprecated")
		debug("getintkey called on %s" % self.humanString())
		if self.color == 'w':
			debug("getintkey: white")
			return self.intkey
		else:
			debug("getintkey: black %s" % -self.intkey)
			return -self.intkey


	def makeChild(self):
		if not self.sterile:
			pos = Pos(self.pos.x,self.pos.y,self.pos.t+1)
			return type(self)(self.color,pos,self.game,self.pieceId,self.age+1)
		else:
			return None











#############################################################
#						PAWN OBJECT							#
#############################################################
class Pawn(Piece):
	def __init__(self, *args):
		self.char = 'p'
		#self.intkey = PAWN
		self.name = "Pawn"
		super(Pawn,self).__init__(*args)

	def getMoves(self):
		"""Show me your moves"""
		moves = []
		hasMoved = not (
					# if your age is the same as your time you can't have time traveled
					(self.pos.t == self.age) and
						((self.color=="b" and self.pos.y==6) or
							(self.color=="w" and self.pos.y==1)))

		#forward movement in space
		pos1 = Pos((self.pos.x,self.pos.y+self.forwards,self.pos.t))
		pos2 = Pos((self.pos.x,self.pos.y+(self.forwards*2),self.pos.t))
		if pos1.isOnBoard():				#if one ahead is on the board
			if self.game.getPieceAt(pos1) is None:	#and it's empty
				moves.append(Move(self.pos,pos1))			#we can move there
				#if we haven't moved yet and 2 ahead is free too, we can go there
				if not hasMoved and (self.game[pos2] is None):
					moves.append(Move(self.pos,pos2))

		#forward movement in time
		pos1 = Pos((self.pos.x,self.pos.y,self.pos.t+1))
		pos2 = Pos((self.pos.x,self.pos.y,self.pos.t+2))
		if pos1.isOnBoard():				#if one ahead is on the board
			if self.game.getPieceAt(pos1) is None:	#and it's empty
				moves.append(Move(self.pos,pos1))			#we can move there
				#if we haven't moved yet and 2 ahead is free too, we can go there
				if not hasMoved and (self.game[pos2] is None):
					moves.append(Move(self.pos,pos2))

		# The taking positions in space
		for tpos in [	Pos(self.pos.x+1,self.pos.y+self.forwards,self.pos.t),
						Pos(self.pos.x-1,self.pos.y+self.forwards,self.pos.t)]:
#			debug("testing %s"%tpos)
			piece = self.game[tpos]
#			debug("contains %s"%piece)
#			debug(self.game.getBoardAtTurn(self.pos.t).prettyPrint())
			if (piece is not None) and (piece.color != self.color):
				moves.append(Move(self.pos,tpos))

		# The taking positions in time
		for tpos in [	Pos((self.pos.x+1,self.pos.y,self.pos.t+1)),
						Pos((self.pos.x-1,self.pos.y,self.pos.t+1))]:
#			piece = self.game[tpos]
#			if (piece is not None) and (piece.color != self.color):
#				moves.append(Move(self.pos,tpos))
			if tpos.isOnBoard():
				moves.append(Move(self.pos,tpos))
		
#		debug("Possible moves for %s at %s: %s" %
#				(self.humanString(),self.pos,[str(m) for m in moves]))
		return moves

	def makeChild(self):
		if not self.sterile:
			pos = Pos(self.pos.x,self.pos.y,self.pos.t+1)
			# if you get to the end, you become a queen
			if (self.pos.y==0 or self.pos.y==7):
				return Queen(self.color,pos,self.game,self.pieceId,self.age+1)
			else:
				return Pawn(self.color,pos,self.game,self.pieceId,self.age+1)
		else:
			return None

#############################################################
#						ROOK OBJECT							#
#############################################################
class Rook(Piece):
	def __init__(self, *args):
		self.char = 'r'
		#self.intkey = ROOK
		self.name = "Rook"
		super(Rook,self).__init__(*args)

	def getMoves(self):
		return self.getMovesInDirections([	(0,0,1),(0,0,-1),
											(0,1,0),(0,-1,0),
											(1,0,0),(-1,0,0),
										])

#############################################################
#						KNIGHT OBJECT						#
#############################################################
class Knight(Piece):
	def __init__(self, *args):
		self.char = 'n'
		#self.intkey = KNIGHT
		self.name = "Knight"
		super(Knight,self).__init__(*args)

	def getMoves(self):
		dirs = [(-1,-2,0),(-1,2,0),(1,-2,0),(1,2,0),(-2,-1,0),(-2,1,0),(2,-1,0),(2,1,0),
				(-1,0,-2),(-1,0,2),(1,0,-2),(1,0,2),(-2,0,-1),(-2,0,1),(2,0,-1),(2,0,1),
				(0,-1,-2),(0,-1,2),(0,1,-2),(0,1,2),(0,-2,-1),(0,-2,1),(0,2,-1),(0,2,1)]
		moves = []
		for dx, dy, dt in dirs:
			npos = Pos((self.pos.x+dx, self.pos.y+dy, self.pos.t+dt))
			if npos.isOnBoard():
				piece = self.game.getPieceAt(npos)
				if piece is None or (piece.color != self.color):
					moves.append(Move(self.pos,npos))
		return moves
			

#############################################################
#						BISHOP OBJECT						#
#############################################################
class Bishop(Piece):
	def __init__(self, *args):
		self.char = 'b'
		#self.intkey = BISHOP
		self.name = "Bishop"
		super(Bishop,self).__init__(*args)

	def getMoves(self):
#		print self.game[Pos("b7t2")]
#		print self.game.getPieceAt(Pos("b7t2"))
		return self.getMovesInDirections([
														( 0, 1, 1),
											(-1, 0, 1),				( 1, 0, 1),
														( 0,-1, 1),

											(-1, 1, 0),				( 1, 1, 0),

											(-1,-1, 0),				( 1,-1, 0),

														( 0, 1,-1),
											(-1, 0,-1),				( 1, 0,-1),
														( 0,-1,-1)
										])

#############################################################
#						QUEEN OBJECT						#
#############################################################
class Queen(Piece):
	def __init__(self, *args):
		self.char = 'q'
		#self.intkey = QUEEN
		self.name = "Queen"
		super(Queen,self).__init__(*args)

	def getMoves(self):
		return self.getMovesInDirections([		(-1,-1,0),
									(-1, 0,-1), (-1, 0,0),(-1, 0,1),
												(-1, 1,0),

									( 0,-1,-1), ( 0,-1,0),( 0,-1,1),
									( 0, 0,-1),           ( 0, 0,1),
									( 0, 1,-1), ( 0, 1,0),( 0, 1,1),

												( 1,-1,0),
									( 1, 0,-1), ( 1, 0,0),( 1, 0,1),
												( 1, 1,0)          ])

#############################################################
#						KING OBJECT						#
#############################################################
class King(Piece):
	def __init__(self, *args):
		self.char = 'k'
		#self.intkey = KING
		self.name = "King"
		super(King,self).__init__(*args)


	def getMoves(self):
		dirs = [				(-1,-1,0),
					(-1, 0,-1), (-1, 0,0),(-1, 0,1),
								(-1, 1,0),

					( 0,-1,-1), ( 0,-1,0),( 0,-1,1),
					( 0, 0,-1),           ( 0, 0,1),
					( 0, 1,-1), ( 0, 1,0),( 0, 1,1),

								( 1,-1,0),
					( 1, 0,-1), ( 1, 0,0),( 1, 0,1),
								( 1, 1,0)          ]
		moves = []
		for dx, dy, dt in dirs:
			npos = Pos((self.pos.x+dx, self.pos.y+dy, self.pos.t+dt))
			if npos.isOnBoard():
				piece = self.game.getPieceAt(npos)
				if piece is None or (piece.color != self.color):
					moves.append(Move(self.pos,npos))
		return moves
